#include "iConfig.hpp"

#include <glf/remote/canvas.h>


#include <glf/app.h>
#include <glf/core/timer.h>
#include <glf/math/constants.h>

#include <math.h>

using namespace glf;
using namespace remote;

Canvas::Canvas() :
mLastUpdateTimeMS(0),
mCurrentRenderer(0),
mCurrentType(Renderer::PT_UNDEFINED),
mCurrentVertexCount(0),
mCurrentColor(Vec4f(1.0f, 1.0f, 1.0f, 1.0f)),
mCurrentOrientation(0.0f)
{
	cvColor(COLOR_WHITE);
}

Point<float> Canvas::ConvertPosScreen2Canvas(Point<short> pos)
{
	Point<float> newPos = glf::App::GetInstance()->ConvertPosScreenToNormalizedDevice(pos);
	newPos.x = -1.0f + 2.0f * newPos.x;
	newPos.y = 1.0f - 2.0f * newPos.y;
	return newPos;
}

Point<float> Canvas::ConvertLength2Size(float length)
{
	int width, height;
	glf::App::GetInstance()->GetWindowSize(width, height);
	Point<float> newLength;
	if (width > height) {
		newLength.x = length;
		newLength.y = length * width / height;
	}
	else {
		newLength.x = length * height / width;
		newLength.y = length;
	}
	return newLength;
}

void Canvas::PostDrawShape(const Shape &shape)
{
	mShapes.push_back(shape);
}

void Canvas::PostSignal(Color color, bool micro)
{
	Canvas::Shape shape;
	shape.mFill = true;
	shape.mType = Canvas::ST_RECT;
	if (micro)
	{
		shape.mPos = Point<float>(-1.0f, -1.0f);
		shape.mSize = Point<float>(2.0f, 0.1f);		
		shape.mDurationMS = 100;
	}
	else
	{
		shape.mPos = Point<float>(-1.0f, -1.0f);
		shape.mSize = Point<float>(2.0f, 2.0f);
		shape.mDurationMS = 1000;
	}
	shape.mColor = color;
	PostDrawShape(shape);
}

void Canvas::Update(Renderer *renderer)
{
	glf::App *app = glf::App::GetInstance();
	if (app->Prepare(0)) {
		//draw on main screen
		int width, height;
		app->GetWindowSize(width, height);
		if (width * height == 0 || !renderer) {
			mShapes.clear();
			return;
		}	
		//start rendering		
		mCurrentRenderer = renderer;
		mCurrentRenderer->Begin2D(width, height); 
		//rendering
		const float mCurrentTimeMS = GetMicroseconds() * 0.001f;
		if (mLastUpdateTimeMS == 0)
			mLastUpdateTimeMS = mCurrentTimeMS;
		const float elapsedTimeMS = mCurrentTimeMS - mLastUpdateTimeMS;
		mLastUpdateTimeMS = mCurrentTimeMS;
		std::list<Shape>::iterator it = mShapes.begin();
		while (it != mShapes.end()) {
			Shape &shape = *it;
			float alpha = glf::Sqrt(Min(1.0f, shape.mDurationMS * 0.002f));
			cvDrawShape(shape, alpha);
			shape.mDurationMS -= elapsedTimeMS;
			if (shape.mDurationMS <= 0.0f)
				it = mShapes.erase(it);
			else
				++it;
		}
		//stop rendering
		mCurrentRenderer->End2D();
		mCurrentRenderer = 0;
		//flush
		app->Present(0);	
	}	
#if 1
	if (app->Prepare(1)) {
		// Render a colored square on the second screen
		int width, height;
		app->GetWindowSize(width, height, 1);
		if (width * height == 0)
			return;	
		//start rendering	
		mCurrentRenderer = renderer;
		mCurrentRenderer->Begin2D(width, height); 
		//rendering
		Shape shape;
		shape.mFill = true;
		shape.mType = Canvas::ST_RECT;
		shape.mPos = Point<float>(-0.25f, -0.25f);
		shape.mSize = Point<float>(0.5f, 0.5f);
		shape.mColor = mShapes.size() > 0 ? Canvas::COLOR_RED : Canvas::COLOR_GREEN;
		cvDrawShape(shape, 1.0f);
		//stop rendering
		mCurrentRenderer->End2D();
		mCurrentRenderer = 0;
		//flush
		app->Present(1);
	}
#endif
}

void Canvas::cvDrawShape(const Shape &shape, float alpha)
{
	cvColor(shape.mColor, shape.mAlpha * alpha);	
	cvOrientation(shape.mOrientation);
	switch (shape.mType)
	{
	case ST_CIRCLE:
		cvCircle2fv((const float *)shape.mPos, (const float *)shape.mSize, shape.mFill);
		break;
	case ST_CROSS:
		cvCross2fv((const float *)shape.mPos, (const float *)shape.mSize, shape.mFill);
		break;
	case ST_POINT:
		cvPoint2fv((const float *)shape.mPos, shape.mFill);
		break;
	case ST_RECT:
		{
			Point<float> p2 = shape.mPos + shape.mSize;
			cvRect2fv((const float *)shape.mPos, (const float *)p2, shape.mFill);
		}
		break;
	case ST_TRIANGLE:
		cvTriangle2fv((const float *)shape.mPos, (const float *)shape.mSize, shape.mFill);
		break;
	}
}

void Canvas::cvBegin(Renderer::PrimitiveType type)
{
	if (mCurrentType != -1) {
		GLF_ASSERT(!"cvEnd() has not be called");
		return;
	}
	mCurrentVertexCount = 0;
	mCurrentType = type;
}

void Canvas::cvEnd()
{
	if (mCurrentType == -1) {
		GLF_ASSERT(!"cvBegin() has not be called");
		return;
	}
	mCurrentRenderer->DrawPrimitive(mCurrentType, &mCurrentVertices[0].x, mCurrentVertexCount, mCurrentColor, mCurrentOrientation);	
	mCurrentType = Renderer::PT_UNDEFINED;
}

void Canvas::cvColor(Canvas::Color color, float alpha)
{
	Vec4f color4f;
	switch (color) 
	{
	case Canvas::COLOR_WHITE:
		color4f = Vec4f(1.0f, 1.0f, 1.0f, alpha);
		break;
	case Canvas::COLOR_GRAY:
		color4f = Vec4f(0.5f, 0.5f, 0.5f, alpha);
		break;
	case Canvas::COLOR_RED:
		color4f = Vec4f(1.0f, 0.0f, 0.0f, alpha);
		break;
	case Canvas::COLOR_GREEN:
		color4f = Vec4f(0.0f, 1.0f, 0.0f, alpha);
		break;
	case Canvas::COLOR_BLUE:
		color4f = Vec4f(0.0f, 0.0f, 1.0f, alpha);
		break;	
	default:
		color4f = Vec4f(1.0f, 1.0f, 1.0f, alpha);
		break;
	}
	mCurrentColor = color4f;
}

void Canvas::cvOrientation(OrientationType orientation)
{
	switch (orientation)
	{
	case OT_TOP: mCurrentOrientation = 0.0f; break;
	case OT_LEFT: mCurrentOrientation = 90.0f; break;
	case OT_BOTTOM: mCurrentOrientation = 180.0f; break;
	case OT_RIGHT: mCurrentOrientation = 270.0f; break;
	default:
		mCurrentOrientation = 0.0f;
		break;
	}
}

void Canvas::cvVertex2f(float x, float y, float u, float v)
{
	if (mCurrentType == -1) {
		GLF_ASSERT(!"glBegin() has not be called");
		return;
	}	
	if (mCurrentVertexCount+1 > mCurrentVertices.size())
		mCurrentVertices.resize(mCurrentVertexCount+1);
	float *p = &mCurrentVertices[mCurrentVertexCount].x;
	p[0] = x;
	p[1] = y;
	p[2] = u;
	p[3] = v;
	mCurrentVertexCount++;
}

void Canvas::cvCircle2fv(const float *p, const float *r, bool fill)
{
	const int segmentCount = 16;
	const float index2rad = 2.0f * PI / segmentCount;
	cvBegin(fill ? Renderer::PT_TRIANGLE_FAN : Renderer::PT_LINE_LOOP);
	if (fill)
		cvVertex2f(p[0], p[1]);
	const int max = fill ? segmentCount + 1 : segmentCount;
	for (int i = 0; i < max; ++i) { 
		const float x = r[0] * glf::Cos(i * index2rad);
		const float y = r[1] * glf::Sin(i * index2rad);
		cvVertex2f(p[0] + x, p[1] + y);
	}
	cvEnd();
}

void Canvas::cvCross2fv(const float *p, const float *r, bool fill)
{
	cvBegin(fill ? Renderer::PT_LINES : Renderer::PT_POINTS);
	if (!fill)
		cvVertex2f(p[0], p[1]);
	cvVertex2f(p[0] - r[0], p[1]);
	cvVertex2f(p[0] + r[0], p[1]);
	cvVertex2f(p[0], p[1] - r[1]);
	cvVertex2f(p[0], p[1] + r[1]);
	cvEnd();
}

void Canvas::cvPoint2fv(const float *p, bool fill)
{
	cvBegin(Renderer::PT_POINTS);
	cvVertex2f(p[0], p[1]);
	cvEnd();
}

void Canvas::cvRect2fv(const float *p1, const float *p2, bool fill)
{
	cvBegin(fill ? Renderer::PT_TRIANGLE_FAN: Renderer::PT_LINE_LOOP);
	cvVertex2f(p1[0], p1[1], 0, 0);
	cvVertex2f(p2[0], p1[1], 1, 0);
	cvVertex2f(p2[0], p2[1], 1, 1);
	cvVertex2f(p1[0], p2[1], 0, 1);		
	cvEnd();
}

void Canvas::cvTriangle2fv(const float *p, const float *r, bool fill)
{
	cvBegin(fill ? Renderer::PT_TRIANGLES: Renderer::PT_LINE_LOOP);
	cvVertex2f(p[0] - r[0] * 0.5f, p[1] - r[1] * 0.5f);
	cvVertex2f(p[0] + r[0] * 0.5f, p[1] - r[1] * 0.5f);
	cvVertex2f(p[0], p[1] + r[1] * 0.5f);
	cvEnd();
}

